package impl

import (
	"context"
	"strings"

	"gitee.com/go-course/go9/projects/devcloud/cmdb/apps/resource"
	"gitee.com/go-course/go9/projects/devcloud/cmdb/common/logger"
)

// 资源保存,及支持更新也支持创建
func (i *impl) Put(ctx context.Context, in *resource.Resource) (
	ins *resource.Resource, err error) {
	// 1. 启动一个事务, 在一个事务里面执行多个对象的保存
	tx := i.db.WithContext(ctx).Begin()
	defer func() {
		if err != nil {
			rerr := tx.Rollback().Error
			if rerr != nil {
				logger.L().Error().Msgf("rollback error, %s", err)
			}
		} else {
			cerr := tx.Commit().Error
			if cerr != nil {
				logger.L().Error().Msgf("commit error, %s", err)
			}
		}
	}()

	in.MakeContentHash()

	// meta保存, 同时保存 meta与hash
	meta := &ResourceMeta{in.Meta, in.ContentHash}
	err = tx.Create(meta).Error
	if err != nil {
		if strings.Contains(err.Error(), "Duplicate entry") {
			err = tx.Updates(meta).Error
		}

		if err != nil {
			return nil, err
		}
	}

	// 保存资源的价格
	if in.Spec != nil {
		spec := &struct {
			ResourceId string `gorm:"primaryKey"`
			*resource.Spec
		}{
			in.Meta.Id,
			in.Spec,
		}
		err = tx.Create(spec).Error
		if err != nil {
			if strings.Contains(err.Error(), "Duplicate entry") {
				err = tx.Updates(spec).Error
			}
			if err != nil {
				return nil, err
			}
		}
	}

	// 保存资源的价格
	// 通过匿名结构体嵌套 实现继承，继承后的对象(Object), 来和数据库的关系进行映射
	if in.Cost != nil {
		cost := struct {
			ResourceId string
			*resource.Cost
		}{
			in.Meta.Id,
			in.Cost,
		}
		err = tx.Create(cost).Error

		if err != nil {
			if strings.Contains(err.Error(), "Duplicate entry") {
				err = tx.Updates(cost).Error
			}
			if err != nil {
				return nil, err
			}
		}
	}

	// 保存资源的状态
	if in.Status != nil {
		status := struct {
			ResourceId string
			*resource.Status
		}{
			in.Meta.Id,
			in.Status,
		}
		err = tx.Create(status).Error
		if err != nil {
			return nil, err
		}
		if err != nil {
			if strings.Contains(err.Error(), "Duplicate entry") {
				err = tx.Updates(status).Error
			}
			if err != nil {
				return nil, err
			}
		}
	}

	// 保存Tag, 全量保存行不行? 不行<云商的Tag>
	// 清除云商的标签, 再补充云商的标签
	// 清除 resource_id: 001, 标签的类型实现云商
	if in.HasTag() {
		err = tx.
			Model(&resource.Tag{}).
			Where("purpose=?", resource.TAG_PURPOSE_THIRDPARTY).
			Where("resource_id=?", in.Meta.Id).
			Delete(&resource.Tag{}).
			Error
		if err != nil {
			return nil, err
		}
		// 保存新同步过来的Tag
		for i := range in.Tags {
			tag := struct {
				ResourceId string
				*resource.Tag
			}{
				in.Meta.Id,
				in.Tags[i],
			}
			err = tx.Create(tag).Error
			if err != nil {
				if strings.Contains(err.Error(), "Duplicate entry") {
					err = tx.Updates(tag).Error
				}
				if err != nil {
					return nil, err
				}
			}
		}
	}

	return
}

// 资源搜索
func (i *impl) Search(ctx context.Context, in *resource.SearchRequest) (
	*resource.ResourceSet, error) {
	// 1. 进行连表
	query := i.db.
		WithContext(ctx).
		// 注意: meta,spec,status,cost 是一一对应的, 不会出现一个资源多个cost
		// tag 是一对多，一个资源 可以有多个Tag
		Select("meta.*,spec.*,status.*,cost.*").
		Table("resource_meta meta").
		Joins("LEFT JOIN resource_spec spec ON spec.resource_id=meta.id").
		Joins("LEFT JOIN resource_status status ON status.resource_id=meta.id").
		Joins("LEFT JOIN resource_cost cost ON cost.resource_id=meta.id")

	// 补充基于标签的过滤
	if len(in.Tag) > 0 {
		query = query.Joins("LEFT JOIN resource_tag tag ON tag.resource_id=meta.id")
		for k, v := range in.Tag {
			query = query.Where("tag.t_key = ? AND tag.t_value = ?", k, v)
		}
	}

	//  过滤条件
	if in.Domain != "" {
		query = query.Where("meta.domain = ?", in.Domain)
	}
	if in.Namespace != "" {
		query = query.Where("meta.namespace = ?", in.Namespace)
	}
	if in.Env != "" {
		query = query.Where("meta.env = ?", in.Env)
	}
	if in.Vendor != nil {
		query = query.Where("spec.vendor = ?", *in.Vendor)
	}
	if in.Keywords != "" {
		query = query.Where("spec.name LIKE ? OR status.private_address LIKE ? OR status.public_address LIKE ?",
			"%"+in.Keywords+"%",
			`"`+in.Keywords+"%",
			`"`+in.Keywords+"%",
		)
	}

	// 处理分页
	query = query.
		Offset(int(in.Page.ComputeOffset())).
		Limit(int(in.Page.PageSize))

	// 获取数据
	res := []struct {
		*resource.Meta
		*resource.Spec
		*resource.Status
		*resource.Cost
	}{}
	err := query.Find(&res).Error
	if err != nil {
		return nil, err
	}

	set := resource.NewResourceSet()
	for i := range res {
		item := res[i]
		r := resource.NewResource()
		r.Meta = item.Meta
		r.Spec = item.Spec
		r.Status = item.Status
		r.Cost = item.Cost
		set.Add(r)
	}

	// 如果需要补充资源的tag
	if in.WithTags {
		// 再单独查询一次tag表就可以了
	}

	return set, nil
}
